package com.wxzhou.wangzhuan.service;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.quarkus.redis.datasource.ReactiveRedisDataSource;
import io.quarkus.redis.datasource.hash.ReactiveHashCommands;
import io.quarkus.redis.datasource.hash.ReactiveHashScanCursor;
import io.quarkus.redis.datasource.keys.ReactiveKeyCommands;
import io.smallrye.mutiny.Uni;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import org.jboss.logging.Logger;

import java.time.Duration;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.HashMap;
import java.util.Map;

/**
 * 阅读redis服务
 */
@ApplicationScoped
public class ReaderRedisService {

    private static final Logger LOG = Logger.getLogger(ReaderRedisService.class);
    private static final String READER_TOKEN_PREFIX = "reader:";
    private static final String READER_JKEY_PREFIX = "reader:jkey:";

    //最大保留24小时
    private static final long DEFAULT_EXPIRE = Duration.ofMinutes(10).getSeconds();
    private static final long JKEY_EXPIRE = Duration.ofMinutes(240).getSeconds();

    private final ReactiveKeyCommands<String> keyCommands;
    private final ReactiveHashCommands<String, String, String> hashCommands;


    private final ObjectMapper objectMapper;

    @Inject
    public ReaderRedisService(ReactiveRedisDataSource redisDataSource, ObjectMapper objectMapper) {
        this.hashCommands = redisDataSource.hash(String.class, String.class, String.class);
        keyCommands = redisDataSource.key(String.class);
        this.objectMapper = objectMapper;
        this.objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL).configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
    }

    /**
     * 存储data到redis中,自动分组
     *
     * @param userid 用户ID
     * @return Uni<Boolean> 存储是否成功
     */
    public Uni<Boolean> storeReaderToken(String app_channel, String deviceId, String adidsn, int sense, String token, Long userid) {
        if (adidsn == null || sense < 0 || userid == null || token == null) {
            LOG.warn("Invalid parameters for storing ad info");
            return Uni.createFrom().item(false);
        }

        try {
            // 构建Redis键
            String redisKey = buildReaderTokenKey(userid);

            // 构建Hash字段名，需要增加时间：
            String hashField = deviceId + "_" + app_channel + "_" + sense + "_" + adidsn;
            Map<String, String> mapResult = new HashMap<>();
            mapResult.put(hashField, token); //存token
            return hashCommands.hset(redisKey, mapResult)
                    .chain(() -> keyCommands.expire(redisKey, DEFAULT_EXPIRE))
                    .onItem().ignore().andContinueWith(false);
        } catch (Exception e) {
            LOG.error("Error storing ad info", e);
            return Uni.createFrom().failure(e);
        }
    }

    //存储jkey
    public Uni<Boolean> storeReaderJkey(String app_channel, String deviceId, String adidsn, int sense, String token, String jkey, Long userid) {
        if (userid == null || jkey == null) {
            LOG.warn("Invalid parameters for storing storeAdInfoBy");
            return Uni.createFrom().item(false);
        }

        try {
            // 构建Redis键
            String redisKey = buildReaderJkeyKey(String.valueOf(userid));
            // 构建Hash字段名，需要增加时间：
            Map<String, String> mapResult = new HashMap<>();
            mapResult.put(token, jkey); //存jkey
            String value = deviceId + "_" + app_channel + "_" + sense + "_" + adidsn + "_jkey0";
            mapResult.put(jkey, value); //存jkey
            return hashCommands.hset(redisKey, mapResult)
                    .chain(() -> keyCommands.expire(redisKey, JKEY_EXPIRE))
                    .onItem().ignore().andContinueWith(false);
        } catch (Exception e) {
            LOG.error("Error storing ad info", e);
            return Uni.createFrom().failure(e);
        }
    }

    /**
     * 根据adidsn，获取用户token
     *
     * @return
     */
    public Uni<String> findTokenAllBy(String app_channel, String deviceId, int sense, Long userid) {
        return Uni.createFrom().voidItem()
                .onItem().transformToUni(unused -> {
                    // 执行hscan命令
                    ReactiveHashScanCursor<String, String> hscan = hashCommands.hscan(buildReaderTokenKey(userid));
                    return hscan.toMulti().collect().asList()
                            .onItem().transformToUni(hashFields -> {
                                String token = null;
                                // 遍历所有的hash字段
                                for (Map.Entry<String, String> entry : hashFields) {
                                    //{deviceId}_{app_channel}_{sense}
                                    String fieldKey = entry.getKey();
                                    //value:存
                                    String jsonValue = entry.getValue();
                                    try {
                                        // 解析fieldKey获取adidsn和sense
                                        if (fieldKey.startsWith(deviceId + "_" + app_channel + "_" + sense + "_")) {
                                            token = jsonValue;
                                            break;
                                        }
                                    } catch (Exception e) {
                                        LOG.error("Failed to process ad report data for field: " + fieldKey, e);
                                    }
                                }
                                return Uni.createFrom().item(token);
                            });
                })
                .onFailure().recoverWithUni(() -> Uni.createFrom().nullItem());
    }

    public Uni<String> findJkeyAllBy(String token, Long userid) {
        return Uni.createFrom().voidItem()
                .onItem().transformToUni(unused -> {
                    // 执行hscan命令
                    ReactiveHashScanCursor<String, String> hscan = hashCommands.hscan(buildReaderJkeyKey(String.valueOf(userid)));
                    return hscan.toMulti().collect().asList()
                            .onItem().transformToUni(hashFields -> {
                                String jkey = null;
                                // 遍历所有的hash字段
                                for (Map.Entry<String, String> entry : hashFields) {
                                    //{deviceId}_{app_channel}_{sense}
                                    String fieldKey = entry.getKey();
                                    //value:存
                                    String jsonValue = entry.getValue();
                                    try {
                                        // 解析fieldKey获取adidsn和sense
                                        if (fieldKey.equals(token)) {
                                            jkey = jsonValue;
                                            break;
                                        }
                                    } catch (Exception e) {
                                        LOG.error("Failed to process ad report data for field: " + fieldKey, e);
                                    }
                                }
                                return Uni.createFrom().item(jkey);
                            });
                })
                .onFailure().recoverWithUni(() -> Uni.createFrom().nullItem());
    }

    //从reader:20251018:45中,找出jkey,找到之后,拿到value，然后执行删除
    //返回deviceId + "_" + app_channel + "_" + sense
    public Uni<String> findAndDelBy(String userid, String jkey) {
        return Uni.createFrom().voidItem()
                .onItem().transformToUni(unused -> {
                    // 执行hscan命令
                    String key = buildReaderJkeyKey(userid);
                    ReactiveHashScanCursor<String, String> hscan = hashCommands.hscan(key);
                    return hscan.toMulti().collect().asList()
                            .onItem().transformToUni(hashFields -> {
                                // 创建一个包装对象来避免Lambda中的final限制
                                final String[] resultHolder = {null};
                                // 遍历所有的hash字段
                                for (Map.Entry<String, String> entry : hashFields) {
                                    //{deviceId}_{app_channel}_{sense}
                                    String fieldKey = entry.getKey();
                                    //value:存
                                    String jsonValue = entry.getValue();
                                    try {
                                        // 解析fieldKey获取adidsn和sense
                                        if (fieldKey.equals(jkey)) {
                                            resultHolder[0] = jsonValue;
                                            break;
                                        }
                                    } catch (Exception e) {
                                        LOG.error("Failed to process ad report data for field: " + fieldKey, e);
                                    }
                                }

                                // 先删除该hash键，然后返回之前读取到的值
                                return hashCommands.hdel(key, jkey)
                                        .onItem().transform(ignore -> resultHolder[0])
                                        .onFailure().recoverWithItem(() -> resultHolder[0]);
                            })
                            .onFailure().recoverWithUni(() -> Uni.createFrom().nullItem());
                });
    }

    /**
     * 构建adIdSn对应的Redis键
     */
    private String buildReaderTokenKey(Long userid) {
        LocalDateTime now = LocalDateTime.now();
        String format = now.format(DateTimeFormatter.ofPattern("yyyyMMdd"));
        return READER_TOKEN_PREFIX + format + ":" + userid;
    }

    private String buildReaderTokenKey(String userid) {
        LocalDateTime now = LocalDateTime.now();
        String format = now.format(DateTimeFormatter.ofPattern("yyyyMMdd"));
        return READER_TOKEN_PREFIX + format + ":" + userid;
    }

    private String buildReaderJkeyKey(String userid) {
        LocalDateTime now = LocalDateTime.now();
        String format = now.format(DateTimeFormatter.ofPattern("yyyyMMdd"));
        return READER_JKEY_PREFIX + format + ":" + userid;
    }
}
